package com.siao.starling.mvc.managers
{
	import com.siao.data.HashMap;
	import com.siao.errors.SingletonError;
	import com.siao.events.NoticeEvent;
	import com.siao.starling.mvc.core.INotice;
	import com.siao.starling.mvc.core.IObserver;
	import com.siao.starling.mvc.core.IPublisher;
	
	/**
	 * 消息管理器
	 * @author SiaoLeon
	 */
	public class NoticeManager implements IPublisher
	{
		// -------------------------------------------------------------------------------------------------------- 静态常量
		
		// -------------------------------------------------------------------------------------------------------- 静态变量
		
		private static var _instance:NoticeManager;
		
		/**
		 * 获取单例对象
		 */
		public static function get instance():NoticeManager
		{
			if (_instance == null)
			{
				_instance = new NoticeManager();
			}
			return _instance;
		}
		
		// -------------------------------------------------------------------------------------------------------- 静态方法
		
		// -------------------------------------------------------------------------------------------------------- 静态函数
		
		// -------------------------------------------------------------------------------------------------------- 属性
		
		private var _observers:HashMap;
		
		/**
		 * 观察者集合
		 * @return
		 */
		public function get observers():HashMap
		{
			return this._observers;
		}
		
		// -------------------------------------------------------------------------------------------------------- 变量
		
		private var processQueue:HashMap;
		
		private var noticeQueue:HashMap;
		
		// -------------------------------------------------------------------------------------------------------- 构造
		
		/**
		 * 构造函数，单例模式
		 */
		public function NoticeManager()
		{
			if (_instance != null)
			{
				throw new SingletonError(this);
			}
			_instance = this;
			
			this.init();
		}
		
		// -------------------------------------------------------------------------------------------------------- 方法
		
		/**
		 * 清除队列消息
		 */
		public function clearQueue():void
		{
			if (this.noticeQueue.size < 1)
			{
				return;
			}
			
			for each (var q:Object in this.noticeQueue.keys)
			{
				var queue:Vector.<INotice> = this.noticeQueue.getValue(q);
				
				if (queue.length <= 0)
				{
					continue;
				}
				
				var notice:INotice = queue[0];
				notice.removeEventListener(NoticeEvent.COMPLETE, onQueueNoticeComplete);
				this.processQueue.remove(notice);
			}
			
			this.noticeQueue.clear();
		}
		
		/**
		 * 清除阻塞消息
		 */
		public function clearBlock():void
		{
			for each (var notice:INotice in this.processQueue.keys)
			{
				if (!notice.isBlock)
				{
					continue;
				}
				
				notice.removeEventListener(NoticeEvent.COMPLETE, onBlockNoticeComplete);
				this.processQueue.remove(notice);
				
				var queue:Vector.<INotice> = this.noticeQueue.getValue(notice.queue);
				
				if (queue.indexOf(notice) == 0)
				{
					queue.shift();
				}
				
				this.processQueueNotice(notice.queue);
			}
		}
		
		public function registerObserver(method:Object, observer:IObserver):void
		{
			if (!this.observers.containsKey(method))
			{
				this.observers.put(method, new Array());
			}
			
			var obs:Array = this.observers.getValue(method);
			
			for (var i:int = 0; i < obs.length; i++)
			{
				var o:IObserver = obs[i];
				
				if (observer.equals(o))
				{
					obs[i] = observer;
					return;
				}
			}
			
			obs.push(observer);
			obs.sortOn("priority", Array.NUMERIC | Array.DESCENDING);
		}
		
		public function removeObserver(method:Object, observer:IObserver):void
		{
			var obs:Array = this.observers.getValue(method);
			var index:int = obs.indexOf(observer);
			
			if (index < 0)
			{
				return;
			}
			
			obs.splice(index, 1);
		}
		
		public function notify(notice:INotice):INotice
		{
			if (!this.observers.containsKey(notice.method))
			{
				return notice;
			}
			
			if (notice.queue == null)
			{
				if (notice.isBlock)
				{
					this.notifyBlockNotice(notice);
				}
				else
				{
					this.notifyNormalNotice(notice);
				}
			}
			else
			{
				if (!this.noticeQueue.containsKey(notice.queue))
				{
					this.noticeQueue.put(notice.queue, new Vector.<INotice>());
				}
				
				var queue:Vector.<INotice> = this.noticeQueue.getValue(notice.queue);
				queue.push(notice);
				
				this.processQueueNotice(notice.queue);
			}
			
			return notice;
		}
		
		// -------------------------------------------------------------------------------------------------------- 函数
		
		/**
		 * 初始化
		 */
		private function init():void
		{
			this._observers = new HashMap();
			this.processQueue = new HashMap();
			this.noticeQueue = new HashMap();
		}
		
		/**
		 * 通知普通消息
		 * @param notice 消息对象
		 */
		private function notifyNormalNotice(notice:INotice):void
		{
			var obs:Array = (this.observers.getValue(notice.method) as Array).concat();
			obs.sortOn("priority", Array.NUMERIC | Array.DESCENDING);
			
			for each (var o:IObserver in obs)
			{
				o.execute(notice);
			}
		}
		
		/**
		 * 处理队列消息
		 */
		private function processQueueNotice(index:Object):void
		{
			if (this.noticeQueue.size < 1)
			{
				return;
			}
			
			var queue:Vector.<INotice> = this.noticeQueue.getValue(index);
			
			if (queue.length < 1)
			{
				this.noticeQueue.remove(index);
				return;
			}
			
			var notice:INotice = queue[0];
			
			if (this.processQueue.containsKey(notice))
			{
				return;
			}
			
			if (notice.isBlock)
			{
				this.notifyBlockNotice(notice);
			}
			else
			{
				notice.addEventListener(NoticeEvent.COMPLETE, onQueueNoticeComplete, false, 0, true);
				this.processQueue.put(notice, (this.observers.getValue(notice.method) as Array).concat());
				this.notifyNormalNotice(notice);
			}
		}
		
		/**
		 * 队列消息处理完成时
		 * @param event
		 */
		private function onQueueNoticeComplete(event:NoticeEvent):void
		{
			var notice:INotice = event.target as INotice;
			
			if (this.processQueue.containsKey(notice))
			{
				var obs:Array = this.processQueue.getValue(notice);
				obs.shift();
				
				if (obs.length < 1)
				{
					notice.removeEventListener(NoticeEvent.COMPLETE, onQueueNoticeComplete);
					this.processQueue.remove(notice);
					
					var queue:Vector.<INotice> = this.noticeQueue.getValue(notice.queue);
					queue.shift();
				}
			}
			
			this.processQueueNotice(notice.queue);
		}
		
		/**
		 * 通知阻塞消息
		 * @param notice 消息对象
		 */
		private function notifyBlockNotice(notice:INotice):void
		{
			if (this.processQueue.containsKey(notice))
			{
				return;
			}
			
			var obs:Array = (this.observers.getValue(notice.method) as Array).concat();
			obs.sortOn("priority", Array.NUMERIC | Array.DESCENDING);
			
			notice.addEventListener(NoticeEvent.COMPLETE, onBlockNoticeComplete, false, 0, true);
			this.processQueue.put(notice, obs);
			this.processBlockNotice(notice);
		}
		
		/**
		 * 处理阻塞消息
		 * @param notice 消息对象
		 */
		private function processBlockNotice(notice:INotice):void
		{
			if (!this.processQueue.containsKey(notice))
			{
				if (notice.queue != null)
				{
					var queue:Vector.<INotice> = this.noticeQueue.getValue(notice.queue);
					queue.shift();
					this.processQueueNotice(notice.queue);
				}
				return;
			}
			
			var obs:Array = this.processQueue.getValue(notice);
			var o:IObserver = obs[0];
			o.execute(notice);
		}
		
		/**
		 * 阻塞消息处理完成时
		 * @param event
		 */
		private function onBlockNoticeComplete(event:NoticeEvent):void
		{
			var notice:INotice = event.target as INotice;
			
			if (this.processQueue.containsKey(notice))
			{
				var obs:Array = this.processQueue.getValue(notice);
				obs.shift();
				
				if (obs.length < 1)
				{
					notice.removeEventListener(NoticeEvent.COMPLETE, onBlockNoticeComplete);
					this.processQueue.remove(notice);
				}
			}
			
			this.processBlockNotice(notice);
		}
	
	}
}
